#pragma once

/**
 * @file mcast_socket.h
 * @brief 多播套接字实现
 * @details 提供用于多播通信的套接字封装，支持数据的发送和接收
 */

#include <functional>

#include "socket_utils.h"

#include "logging.h"

namespace Common {
  /**
   * @namespace Common
   * @brief 包含常用工具类和函数的命名空间
   */
  /**
   * @brief 发送和接收缓冲区的大小（字节）
   * @details 设置为64MB，用于存储多播数据
   */
  constexpr size_t McastBufferSize = 64 * 1024 * 1024;

  /**
   * @struct McastSocket
   * @brief 多播套接字结构
   * @details 提供初始化、加入/离开多播组以及数据发送和接收的功能
   */
  struct McastSocket {
    /**
     * @brief 构造函数
     * @param logger 日志记录器引用
     * @details 初始化多播套接字对象并分配发送和接收缓冲区
     */
    McastSocket(Logger &logger)
        : logger_(logger) {
      outbound_data_.resize(McastBufferSize);
      inbound_data_.resize(McastBufferSize);
    }

    /**
     * @brief 初始化多播套接字
     * @param ip 多播组IP地址
     * @param iface 网络接口名称
     * @param port 端口号
     * @param is_listening 是否以监听模式初始化
     * @return 初始化的套接字文件描述符，失败时返回-1
     * @details 初始化多播套接字以从流中读取或发布到流中，注意这不会加入多播组
     */
    auto init(const std::string &ip, const std::string &iface, int port, bool is_listening) -> int;

    /**
     * @brief 加入多播组
     * @param ip 要加入的多播组IP地址
     * @return 加入成功返回true，失败返回false
     * @details 添加/加入成员资格/订阅多播流
     */
    auto join(const std::string &ip) -> bool;

    /**
     * @brief 离开多播组
     * @param ip 要离开的多播组IP地址
     * @param port 端口号
     * @details 移除/离开成员资格/取消订阅多播流
     */
    auto leave(const std::string &ip, int port) -> void;

    /**
     * @brief 发送和接收数据
     * @return 如果有数据被接收则返回true，否则返回false
     * @details 发布外发数据并读取传入数据，非阻塞方式
     */
    auto sendAndRecv() noexcept -> bool;

    /**
     * @brief 将数据复制到发送缓冲区
     * @param data 要发送的数据指针
     * @param len 数据长度
     * @details 将数据复制到发送缓冲区，但不立即发送，需要调用sendAndRecv()才会实际发送
     */
    auto send(const void *data, size_t len) noexcept -> void;

    int socket_fd_ = -1; /**< 套接字文件描述符，初始化为-1表示无效 */

    /**
     * @brief 发送和接收缓冲区
     * @details 通常只需要其中一个，而不是同时需要两个
     */
    std::vector<char> outbound_data_; /**< 外发数据缓冲区 */
    size_t next_send_valid_index_ = 0; /**< 下一个可用的发送索引位置 */
    std::vector<char> inbound_data_; /**< 入站数据缓冲区 */
    size_t next_rcv_valid_index_ = 0; /**< 下一个可用的接收索引位置 */

    /**
     * @brief 数据读取时的回调函数
     * @details 当数据被读取时调用的方法的函数包装器
     */
    std::function<void(McastSocket *s)> recv_callback_ = nullptr; /**< 接收数据时的回调函数，初始化为空 */

    std::string time_str_; /**< 用于存储时间字符串的缓冲区 */
    Logger &logger_; /**< 日志记录器引用 */
  };
}
